﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using VSeWSS;

namespace Yappi.HierarchicalField
{
    [CLSCompliant(false)]
    [TargetList("5544f072-835d-4461-b2b4-04f9daa84232")]
    [Guid("3ff339e0-ef09-4b6e-be29-96807f2dcb15")]
    public class WorkDirectionsContentTypeItemEventReceiver : SPItemEventReceiver
    {
        /// <summary>
        /// Поле с иерархией.
        /// </summary>
        public static string WorkDirectionHierarchy = "WorkDirectionHierarchy";
        /// <summary>
        /// Поле с отступами, автоматически вычисляется. 
        /// </summary>
        public static string WorkDirectionIndent = "WorkDirectionIndent";
        /// <summary>
        /// Initializes a new instance of the Microsoft.SharePoint.SPItemEventReceiver class.
        /// </summary>
        public WorkDirectionsContentTypeItemEventReceiver()
        {
        }
        #region Переодределенные методы
        /// <summary>
        /// Asynchronous after event that occurs after a new item has been added to its containing object.
        /// </summary>
        /// <param name="properties">
        /// A Microsoft.SharePoint.SPItemEventProperties object that represents properties of the event handler.
        /// </param>
        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public override void ItemAdded(SPItemEventProperties properties)
        {
            this.CheckRulesAfterAddAndUpdate(properties);
        }

        /// <summary>
        /// Событие обновления элемента вызываем проверку для элемента.
        /// </summary>
        /// <param name="properties">Свойства события</param>
        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public override void ItemUpdated(SPItemEventProperties properties)
        {
            this.CheckRulesAfterAddAndUpdate(properties);
        }

        /// <summary>
        /// Удаление элемента
        /// </summary>
        /// <param name="properties">Свойства события</param>
        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public override void ItemDeleting(SPItemEventProperties properties)
        {
            this.CheckRulesDuringDelete(properties);
        }
        #endregion

        #region Статические методы
        /// <summary>
        /// Сортировка списка в обратном порядке
        /// </summary>
        /// <param name="listItem">Элемент списка</param>
        /// <param name="hierarchicalID">Иерархический идентификатор</param>
        private static void ReorderListBack(SPListItem listItem, HierarchicalID hierarchicalID)
        {
            List<HierarchicalIDToIDMapping> sourceHierarchicalIdsList = CreateHierarchicalIdsListWithOrdering(hierarchicalID, listItem);
            sourceHierarchicalIdsList.Reverse();
            if (IsChildExist(hierarchicalID, sourceHierarchicalIdsList[0].HierarchicalID))
                return;
            foreach (HierarchicalIDToIDMapping item in sourceHierarchicalIdsList)
            {
                int index = hierarchicalID.LevelsCount - 1;
                if (item.HierarchicalID.LevelsCount >= hierarchicalID.LevelsCount)
                {
                    item.HierarchicalID[index] = item.HierarchicalID[index] - 1;
                    SPListItem li = listItem.ParentList.GetItemById(item.ID);
                    li[WorkDirectionHierarchy] = item.HierarchicalID.ToString();
                    li.Update();
                }
            }
        }

        /// <summary>
        /// Определяем есть ли потомки у этого элемента.
        /// </summary>
        /// <param name="hierarchicalID">Иерархический идентификатор</param>
        /// <param name="chield">Дочерний элемент</param>
        /// <returns>Содержит или не содержит потомков</returns>
        private static bool IsChildExist(HierarchicalID hierarchicalID, HierarchicalID chield)
        {
            return chield.ToString().StartsWith(hierarchicalID.ToString(), StringComparison.Ordinal);
        }

        /// <summary>
        /// Перестроение элементов списка
        /// </summary>
        /// <param name="listItem">Элемент списка</param>
        /// <param name="hierarchicalID">Иерархический идентификатор</param>
        private static void ReorderList(SPListItem listItem, HierarchicalID hierarchicalID)
        {
            bool hasTwoEqualItems = CheckTwoEqualItems(listItem.ParentList, hierarchicalID);
            if (!hasTwoEqualItems)
                return;
            List<HierarchicalIDToIDMapping> sourceHierarchicalIdsList = CreateHierarchicalIdsListWithOrdering(hierarchicalID, listItem);
            foreach (HierarchicalIDToIDMapping item in sourceHierarchicalIdsList)
            {
                int index = hierarchicalID.LevelsCount - 1;
                if (item.HierarchicalID.LevelsCount >= hierarchicalID.LevelsCount)
                {
                    item.HierarchicalID[index] = item.HierarchicalID[index] + 1;
                    SPListItem li = listItem.ParentList.GetItemById(item.ID);
                    li[WorkDirectionHierarchy] = item.HierarchicalID.ToString();
                    li.Update();
                }
            }
        }

        /// <summary>
        /// Проверка наличия двух элементов с одинаковым hierarchicalID
        /// </summary>
        /// <param name="list">Список в котором проводим проверку</param>
        /// <param name="hierarchicalID">Иерархический идентификатор</param>
        /// <returns>Определяем наличие двух идеентичных элементов</returns>
        private static bool CheckTwoEqualItems(SPList list, HierarchicalID hierarchicalID)
        {
            SPQuery q = new SPQuery();
            string queryFormat = @"<Where>
      <Eq>
         <FieldRef Name='{0}' />
         <Value Type='Text'>{1}</Value>
      </Eq>
   </Where>";
            q.Query = string.Format(CultureInfo.CurrentCulture, queryFormat, WorkDirectionHierarchy, hierarchicalID.ToString());
            SPListItemCollection coll = list.GetItems(q);

            return coll.Count == 2;
        }

        /// <summary>
        /// Создание словаря элементов
        /// </summary>
        /// <param name="hierarchicalID">Идентификатор иерархии</param>
        /// <param name="listItem">Элемент списка</param>
        /// <returns>Словарь иерархических идентификаторов</returns>
        private static List<HierarchicalIDToIDMapping> CreateHierarchicalIdsListWithOrdering(HierarchicalID hierarchicalID, SPListItem listItem)
        {
            List<HierarchicalIDToIDMapping> sourceHierarchicalIdsList = new List<HierarchicalIDToIDMapping>();
            SPQuery query = new SPQuery();
            string queryFormat = @"   <Where>
      <Geq>
         <FieldRef Name='{0}' />
         <Value Type='Text'>{1}</Value>
      </Geq>
   </Where>
<OrderBy>
    <FieldRef Name='{0}' Ascending='TRUE' />
  </OrderBy>";
            query.Query = string.Format(CultureInfo.InvariantCulture, queryFormat, WorkDirectionHierarchy, hierarchicalID.ToString());
            SPListItemCollection listItemCollection = listItem.ParentList.GetItems(query);
            foreach (SPListItem item in listItemCollection)
            {
                if (!listItem.ID.Equals(item.ID))
                {
                    HierarchicalIDToIDMapping itemID = new HierarchicalIDToIDMapping(item[WorkDirectionHierarchy].ToString(), item.ID);
                    sourceHierarchicalIdsList.Add(itemID);
                }
            }

            // сортируем список
            sourceHierarchicalIdsList.Sort();
            sourceHierarchicalIdsList.Reverse();
            return sourceHierarchicalIdsList;
        }
        #endregion

        /// <summary>
        /// Проверка правильности заполнения списка
        /// </summary>
        /// <param name="properties">Свойства события</param>
        private void CheckRulesAfterAddAndUpdate(SPItemEventProperties properties)
        {
            SPListItem listItem = properties.ListItem;
            if (listItem[WorkDirectionHierarchy] != null)
            {
                string originalID = listItem[WorkDirectionHierarchy].ToString();
                HierarchicalID hierarchicalID = new HierarchicalID(originalID);
                string newValue = hierarchicalID.ToString();
                if (originalID != newValue)
                {
                    listItem[WorkDirectionHierarchy] = newValue;
                    listItem.Update();
                }

                int dotCount = hierarchicalID.LevelsCount - 1;
                listItem[WorkDirectionIndent] = dotCount;
                listItem.Update();

                DisableEventFiring();
                ReorderList(listItem, hierarchicalID);
                EnableEventFiring();
            }
        }

        /// <summary>
        /// Проверка правильности списка поле удаления элемента в списке.
        /// </summary>
        /// <param name="properties">Свойства события</param>
        private void CheckRulesDuringDelete(SPItemEventProperties properties)
        {
            SPListItem listItem = properties.ListItem;
            if (listItem[WorkDirectionHierarchy] != null)
            {
                string originalID = listItem[WorkDirectionHierarchy].ToString();
                HierarchicalID hierarchicalID = new HierarchicalID(originalID);
                DisableEventFiring();
                ReorderListBack(listItem, hierarchicalID);
                EnableEventFiring();
            }
        }
    }
}
